Amazon ElastiCache for RedisからAmazon ElastiCache for Valkeyに変更してみた

Amazon ElastiCache for RedisからAmazon ElastiCache for Valkeyに変更してみた

Clock Icon2024.11.10

以下の記事で紹介されている通り、RedisキャッシュからValkeyキャッシュへ変更することが可能ということで試してみました。
https://aws.amazon.com/jp/blogs/news/get-started-with-amazon-elasticache-for-valkey/

やること

上記の記事では「ダウンタイムゼロの移行」と記載されていましたので、変更中に接続が可能なのかと変更前に登録されていたデータがどうなるのかという部分を確認してみます。

変更中の接続確認は安直ですが、redis-cliでpingを実行し続けて変更途中で失敗しているものが無いかというのを確認してみます。

nohup bash -c 'while true; do printf "%s | " "$(date)"; redis-cli -h プライマリエンドポイント -p 6379 ping; done' >> connect.txt &

変更後のデータ確認は変更前に以下のredis-cliコマンドでデータを登録して変更後に同じ値が取得できるか確認します。

redis-cli -h プライマリエンドポイント -p 6379 mset name:1 "kobayashi1" name:2 "kobayashi2" name:3 "kobayashi3"

以下のコマンドを実行した際に「"kobayashi1"」、「"kobayashi2"」、「"kobayashi3"」という値が取得できることを確認します。

redis-cli -h プライマリエンドポイント -p 6379 mget name:1 name:2 name:3

環境作成

まずは実行環境の作成を行います。
AWSリソースの作成は以下のCloudFormationテンプレートで行いました。

AWSTemplateFormatVersion: "2010-09-09"

Description: test Stack

Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------# 
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: 
          default: Parameters for VPC
        Parameters:
          - VPCCIDR
      - Label: 
          default: Parameters for Subnet
        Parameters:
          - PublicSubnet01CIDR
          - PublicSubnet02CIDR
      - Label: 
          default: Parameters for ec2
        Parameters:
          - EC2VolumeSize
          - EC2VolumeIOPS
          - EC2AMI
          - EC2InstanceType
      - Label: 
          default: Parameters for ElastiCache
        Parameters:
          - ElastiCacheNodeType

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------# 
  VPCCIDR:
    Default: 172.30.0.0/16
    Type: String

  PublicSubnet01CIDR:
    Default: 172.30.0.0/24
    Type: String

  PublicSubnet02CIDR:
    Default: 172.30.1.0/24
    Type: String

  EC2VolumeSize:
    Default: 32
    Type: Number

  EC2VolumeIOPS:
    Default: 3000
    Type: Number

  EC2AMI:
    Type : AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64

  EC2InstanceType:
    Default: t3.medium
    Type: String

  ElastiCacheNodeType:
    Default: cache.t4g.small
    Type: String

Resources:
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------# 
  EC2IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: Allow
            Principal: 
              Service: 
                - ec2.amazonaws.com
            Action: 
              - 'sts:AssumeRole'
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      RoleName: iam-role-ec2
      Tags:
        - Key: Name
          Value: iam-role-ec2

  EC2IAMInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: iam-instanceprofile-ec2
      Roles: 
        - !Ref EC2IAMRole

# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------# 
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags: 
        - Key: Name
          Value: test-vpc

# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------# 
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - Key: Name
          Value: !Sub test-igw

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------# 
  PublicSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: !Ref PublicSubnet01CIDR
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: !Sub test-public-subnet-01
      VpcId: !Ref VPC

  PublicSubnet02:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1c
      CidrBlock: !Ref PublicSubnet02CIDR
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: !Sub test-public-subnet-02
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------# 
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: test-public-rtb

  PublicRouteTableRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref PublicRouteTable

  PublicRtAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet01

  PublicRtAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet02

# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------# 
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for ec2
      GroupName: test-sg-ec2
      Tags: 
        - Key: Name
          Value: test-sg-ec2
      VpcId: !Ref VPC

  ElasticacheSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for Redis
      GroupName: test-sg-elasticache
      SecurityGroupIngress:
        - IpProtocol: tcp
          SourceSecurityGroupId: !Ref EC2SG
          FromPort: 6379
          ToPort: 6379
      Tags: 
        - Key: Name
          Value: test-sg-elasticache
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------# 
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      BlockDeviceMappings: 
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            Encrypted: true
            Iops: !Ref EC2VolumeIOPS
            VolumeSize: !Ref EC2VolumeSize
            VolumeType: gp3
      DisableApiTermination: false
      IamInstanceProfile: !Ref EC2IAMInstanceProfile
      ImageId: !Ref EC2AMI
      InstanceType: !Ref EC2InstanceType
      SecurityGroupIds: 
        - !Ref EC2SG
      SubnetId: !Ref PublicSubnet01
      Tags:
        - Key: Name
          Value: test-ec2
      UserData: !Base64 |
        #!/bin/bash
        dnf install gcc openssl-devel -y
        curl -s http://download.redis.io/redis-stable.tar.gz -o redis-stable.tar.gz
        tar zxf redis-stable.tar.gz
        cd redis-stable/
        make redis-cli BUILD_TLS=yes
        install -m 0755 src/redis-cli /usr/local/bin/

# ------------------------------------------------------------#
# Elasticache
# ------------------------------------------------------------# 
  ElastiCacheSubnetGroup:
    Type: AWS::ElastiCache::SubnetGroup
    Properties:
      CacheSubnetGroupName: test-cache-subunet
      Description: Redis Subnet Group
      SubnetIds:
        - !Ref PublicSubnet01
        - !Ref PublicSubnet02

  ElastiCacheParameterGroup:
    Type: AWS::ElastiCache::ParameterGroup
    Properties:
      Description: ElastiCacheParameterGroup
      CacheParameterGroupFamily: redis7

  ElastiCacheCluster:
    Type: AWS::ElastiCache::ReplicationGroup
    Properties: 
      CacheNodeType: !Ref ElastiCacheNodeType
      CacheParameterGroupName: !Ref ElastiCacheParameterGroup
      CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup
      Engine: Redis
      EngineVersion: 7.1
      MultiAZEnabled: true
      NumCacheClusters: 2
      Port: 6379
      ReplicationGroupDescription: Redis Cluster
      ReplicationGroupId: test-elasticache-redis
      SecurityGroupIds: 
        - !Ref ElasticacheSG
      Tags: 
        - Key: Name
          Value: test-elasticache-redis

上記のCloudFormationテンプレートで作成しているリソースは接続確認用EC2インスタンスとElastiCache for Redisになります。

デプロイは以下のAWS CLIコマンドを実行します。
デプロイ完了までに10分~15分程度かかります。(ElastiCacheの作成に少し時間がかかります)

aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM

Valkeyへ変更

下準備

AWSリソースの作成が完了したらEC2インスタンスへ接続して下準備を行います。
EC2へはSystems Manager Session Managerで接続できるようにIAMロールなどの設定を行っていますので、以下のドキュメントの手順で接続してください。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/connect-with-systems-manager-session-manager.html
EC2へ接続ができたら以下のコマンドを実行してElastiCache for Redisに接続できることを確認します。
接続に成功していると「PONG」という出力が確認できます。

redis-cli -h プライマリエンドポイント -p 6379 ping

接続確認ができたら以下のコマンドでデータを登録します。

redis-cli -h プライマリエンドポイント -p 6379 mset name:1 "kobayashi1" name:2 "kobayashi2" name:3 "kobayashi3"

コマンドを実行したら以下のコマンドで登録されていることを確認します。

redis-cli -h プライマリエンドポイント -p 6379 mget name:1 name:2 name:3

登録されていることが確認できたら以下のコマンドを実行します。
プロセスを消すまで「connect.txt」というファイルにpingの実行結果を書き込み続けます。

nohup bash -c 'while true; do printf "%s | " "$(date)"; redis-cli -h プライマリエンドポイント -p 6379 ping; done' >> connect.txt &

Valkeyへ変更

接続確認用EC2で下準備が完了したら実際にValkeyへ変更していきます。
ElastiCacheのコンソールへ移動して左のメニューから「Redis OSS キャッシュ」をクリックします。
クリック後、CloudFormationで作成したRedisクラスターを選択します。
スクリーンショット 2024-11-10 204610

選択後、「アクション」から「変更」をクリックします。
スクリーンショット 2024-11-10 205025

画面遷移後、「クラスター設定」欄にある「モード」を「Valkey」へ変更します。
この際にデフォルトのパラメータグループ以外を設定したい場合は事前にValkey用のパラメータグループを作成しておいてください。
スクリーンショット 2024-11-10 205639

モードを選択したら画面下までスクロールして「変更をプレビュー」をクリックします。
画面遷移後、「変更の概要」欄で「エンジン」が「Valkey」になっていること、「すぐに適用」のチェックボックスにチェックが入っていることを確認したら「変更」をクリックします。
スクリーンショット 2024-11-10 205934

変更をクリック後、ステータスがModifyingになります。
スクリーンショット 2024-11-10 210321

変更が完了すると「Valkey キャッシュ」の画面から確認できるようになります。(変更完了までには20分程度かかりました)
スクリーンショット 2024-11-10 212003

変更が完了したらEC2で実行していたコマンドを停止します。(他にバックグラウンドジョブを実行していなければ以下のコマンドで停止します)

kill -s SIGKILL %1

コマンドを停止したら以下のコマンドで「connect.txt」の行数と「PONG」という文字列が入っている行数が一致するか確認します。

[ "$(cat connect.txt | wc -l)" = "$(grep "PONG" connect.txt | wc -l)" ] && echo "一致しています。" || { echo "一致していません。"; echo "総行数: $(cat connect.txt | wc -l)"; echo "PONG を含む行数: $(grep "PONG" connect.txt | wc -l)"; }

結果としては特にエラー無くpingが成功しているようでした。

[ssm-user@ip-172-30-0-92 ~]$ [ "$(cat connect.txt | wc -l)" = "$(grep "PONG" connect.txt | wc -l)" ] && echo "一致しています。" || { echo "一致していません。"; echo "総行数: $(cat connect.txt | wc -l)"; echo "PONG を含む行数: $(grep "PONG" connect.txt | wc -l)"; }
一致しています。
[ssm-user@ip-172-30-0-92 ~]$ cat connect.txt | wc -l
117305
[ssm-user@ip-172-30-0-92 ~]$ grep "PONG" connect.txt | wc -l
117305

次に変更前に登録していたデータがどのようになっているか以下のコマンドで確認します。

redis-cli -h プライマリエンドポイント -p 6379 mget name:1 name:2 name:3

結果としては変更前に登録していたデータはそのまま残っていました。

[ssm-user@ip-172-30-0-92 ~]$ redis-cli -h test-elasticache-redis.6fkmjm.ng.0001.apne1.cache.amazonaws.com -p 6379 mget name:1 name:2 name:3
1) "kobayashi1"
2) "kobayashi2"
3) "kobayashi3"

さいごに

今回はRedisキャッシュからValkeyキャッシュへの変更を試してみました。
検証の結果としては変更中も接続は可能となっていて変更後もデータは残っていました。
今回の検証はかなり簡易的なものとなっています。
皆様のワークロードによっては結果が変わる可能性があるので、本番環境等の変更を行う場合は必ず事前に検証を行ってください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.